** sample .cto ** /** * My commodity trading network */ namespace org.acme.mynetwork asset Commodity identified by tradingSymbol { o String tradingSymbol o String description o String mainExchange o Double quantity --> Trader owner } participant Trader identified by tradeId { o String tradeId o String firstName o String lastName } transaction Trade { --> Commodity commodity --> Trader newOwner } ** lib/sample.js ** /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Track the trade of a commodity from one trader to another * @param {org.acme.mynetwork.Trade} trade - the trade to be processed * @transaction */ function trade(trade) { trade.commodity.owner = trade.newOwner; return getAssetRegistry('org.acme.mynetwork.Commodity') .then(function (assetRegistry) { return assetRegistry.update(trade.commodity); }); } ** test/sample.js ** 'use strict'; const AdminConnection = require('composer-admin').AdminConnection; const BrowserFS = require('browserfs/dist/node/index'); const BusinessNetworkConnection = require('composer-client').BusinessNetworkConnection; const BusinessNetworkDefinition = require('composer-common').BusinessNetworkDefinition; const path = require('path'); require('chai').should(); const bfs_fs = BrowserFS.BFSRequire('fs'); const NS = 'org.acme.mynetwork'; describe('Commodity Trading', () => { // let adminConnection; let businessNetworkConnection; before(() => { BrowserFS.initialize(new BrowserFS.FileSystem.InMemory()); const adminConnection = new AdminConnection({ fs: bfs_fs }); return adminConnection.createProfile('defaultProfile', { type: 'embedded' }) .then(() => { return adminConnection.connect('defaultProfile', 'admin', 'adminpw'); }) .then(() => { return BusinessNetworkDefinition.fromDirectory(path.resolve(__dirname, '..')); }) .then((businessNetworkDefinition) => { return adminConnection.deploy(businessNetworkDefinition); }) .then(() => { businessNetworkConnection = new BusinessNetworkConnection({ fs: bfs_fs }); return businessNetworkConnection.connect('defaultProfile', 'my-network', 'admin', 'adminpw'); }); }); describe('#tradeCommodity', () => { it('should be able to trade a commodity', () => { const factory = businessNetworkConnection.getBusinessNetwork().getFactory(); // create the traders const dan = factory.newResource(NS, 'Trader', 'dan@email.com'); dan.firstName = 'Dan'; dan.lastName = 'Selman'; const simon = factory.newResource(NS, 'Trader', 'simon@email.com'); simon.firstName = 'Simon'; simon.lastName = 'Stone'; // create the commodity const commodity = factory.newResource(NS, 'Commodity', 'EMA'); commodity.description = 'Corn'; commodity.mainExchange = 'Euronext'; commodity.quantity = 100; commodity.owner = factory.newRelationship(NS, 'Trader', dan.$identifier); // create the trade transaction const trade = factory.newTransaction(NS, 'Trade'); trade.newOwner = factory.newRelationship(NS, 'Trader', simon.$identifier); trade.commodity = factory.newRelationship(NS, 'Commodity', commodity.$identifier); // the owner should of the commodity should be dan commodity.owner.$identifier.should.equal(dan.$identifier); // Get the asset registry. return businessNetworkConnection.getAssetRegistry(NS + '.Commodity') .then((assetRegistry) => { // add the commodity to the asset registry. return assetRegistry.add(commodity) .then(() => { return businessNetworkConnection.getParticipantRegistry(NS + '.Trader'); }) .then((participantRegistry) => { // add the traders return participantRegistry.addAll([dan, simon]); }) .then(() => { // submit the transaction return businessNetworkConnection.submitTransaction(trade); }) .then(() => { return businessNetworkConnection.getAssetRegistry(NS + '.Commodity'); }) .then((assetRegistry) => { // re-get the commodity return assetRegistry.get(commodity.$identifier); }) .then((newCommodity) => { // the owner of the commodity should not be simon newCommodity.owner.$identifier.should.equal(simon.$identifier); }); }); }); }); }); ** package.json ** { "name": "my-network", "version": "0.0.1", "description": "My very first hyperledger composer network", "scripts": { "prepublish": "mkdirp ./dist && composer archive create --sourceType dir --sourceName . -a ./dist/my-network.bna", "pretest": "npm run lint", "lint": "eslint .", "postlint": "npm run licchk", "licchk":"echo license-check", "postlicchk": "npm run doc", "doc": "jsdoc --pedantic --recurse -c jsdoc.json", "test-inner": "mocha -t 0 --recursive && cucumber-js", "test-cover": "nyc npm run test-inner", "test": "npm run test-inner" }, "repository": { "type": "git", "url": "https://github.com/hyperledger/composer-sample-networks.git" }, "keywords": [ "sample", "composer", "composer-network" ], "author": "Hyperledger Composer", "license": "Apache-2.0", "devDependencies": { "browserfs": "^1.2.0", "chai": "^3.5.0", "chai-as-promised": "^6.0.0", "composer-admin": "^0.9.0", "composer-cli": "^0.9.0", "composer-client": "^0.9.0", "composer-connector-embedded": "^0.9.0", "composer-cucumber-steps": "^0.9.0", "cucumber": "^2.2.0", "eslint": "^3.6.1", "istanbul": "^0.4.5", "jsdoc": "^3.4.1", "license-check": "^1.1.5", "mkdirp": "^0.5.1", "mocha": "^3.2.0", "moment": "^2.17.1", "nyc": "^11.0.2" }, "license-check-config": { "src": [ "**/*.js", "!./coverage/**/*", "!./node_modules/**/*", "!./out/**/*", "!./scripts/**/*" ], "path": "header.txt", "blocking": true, "logInfo": false, "logError": true }, "nyc": { "exclude": [ "coverage/**", "features/**", "out/**", "test/**" ], "reporter": [ "text-summary", "html" ], "all": true, "check-coverage": true, "statements": 100, "branches": 100, "functions": 100, "lines": 100 } }